package main

import (
	"bufio"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/sqweek/dialog"
)

func main() {
	// 1. 选择文件夹路径
	folderPath, err := dialog.Directory().Title("选择保存图片的文件夹").Browse()
	if err != nil {
		fmt.Println("无法选择文件夹:", err)
		return
	}
	fmt.Println("选择的文件夹路径:", folderPath)

	// 2. 从命令行输入图片链接列表
	fmt.Println("请输入图片链接列表（每行一个链接，输入空行结束）：")
	scanner := bufio.NewScanner(os.Stdin)
	var urls []string
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if line == "" {
			break
		}
		urls = append(urls, line)
	}

	// 3. 使用 WaitGroup 和 Goroutine 异步下载图片
	var wg sync.WaitGroup
	// 限制并发 goroutine 数量
	maxGoroutines := 3
	guard := make(chan struct{}, maxGoroutines) // 用于控制并发数量的 channel

	successList := make([]string, 0)
	failList := make([]string, 0)
	var mu sync.Mutex

	fileNameLen := len(fmt.Sprintf("%d", len(urls)))

	for i, url := range urls {
		wg.Add(1)
		guard <- struct{}{} // 占用一个并发槽

		go func(index int, url string, fileNameLen int) {
			// time.Sleep(3 * time.Second)

			defer wg.Done()
			defer func() { <-guard }() // 释放并发槽

			// 根据链接判断文件名后缀
			fileName := fmt.Sprintf("%0*d%s", fileNameLen, index+1, getFileExtension(url))
			filePath := filepath.Join(folderPath, fileName)

			err := downloadImage(url, filePath)
			mu.Lock()
			if err != nil {
				failList = append(failList, fmt.Sprintf("%s (错误: %v)", url, err))
			} else {
				successList = append(successList, url)
			}
			mu.Unlock()
		}(i, url, fileNameLen)
	}

	// 等待所有 Goroutine 完成
	wg.Wait()

	// 4. 打印完成详情
	fmt.Println("\n下载任务完成！")
	fmt.Println("成功的任务：")
	for i, success := range successList {
		fmt.Printf("- %d: %s\n", i+1, success)
	}

	if len(failList) != 0 {
		fmt.Println("\n失败的任务：")
		for i, fail := range failList {
			fmt.Printf("- %d: %s\n", i+1, fail)
		}
	}

}

// 下载图片并保存到指定路径
func downloadImage(url, filePath string) error {
	// 创建一个新的 HTTP 请求
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return fmt.Errorf("创建请求失败: %v", err)
	}

	// 设置浏览器常见的请求头
	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
	req.Header.Set("Accept", "image/webp,image/apng,image/*,*/*;q=0.8")
	req.Header.Set("Accept-Language", "en-US,en;q=0.9")

	// 发送请求
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return fmt.Errorf("请求失败: %v", err)
	}
	defer resp.Body.Close()

	// 检查响应状态码
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("请求失败，状态码: %d", resp.StatusCode)
	}

	// 创建文件
	file, err := os.Create(filePath)
	if err != nil {
		return fmt.Errorf("无法创建文件: %v", err)
	}
	defer file.Close()

	// 将响应内容写入文件
	_, err = io.Copy(file, resp.Body)
	if err != nil {
		return fmt.Errorf("写入文件失败: %v", err)
	}

	return nil
}

// 根据链接判断文件名后缀
func getFileExtension(url string) string {
	// 默认使用 .jpg
	extension := ".jpg"

	// 查找最后一个斜杠和点号的位置
	lastSlashIndex := strings.LastIndex(url, "/")
	lastDotIndex := strings.LastIndex(url, ".")

	// 如果链接中包含点号，并且点号在最后一个斜杠之后，则提取后缀
	if lastDotIndex != -1 && lastDotIndex > lastSlashIndex {
		// 提取从点号开始到字符串末尾的部分
		extension = url[lastDotIndex:]

		// 去除查询参数和片段标识符
		if queryIndex := strings.IndexAny(extension, "?#"); queryIndex != -1 {
			extension = extension[:queryIndex]
		}
	}

	// 确保后缀是常见的图片格式
	switch strings.ToLower(extension) {
	case ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp":
		return extension
	default:
		// 如果不是常见的图片格式，则使用默认的 .jpg
		return ".jpg"
	}
}
